package com.springbootx.mybatis.config;

import com.springbootx.mybatis.config.transactional.ConnectWarp;
import com.springbootx.mybatis.config.transactional.Copy;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @author Tony.niu (hystrix0779@yeah.net)
 * @version V1.0
 * @date 2020/6/30
 **/
public class DataSourceRouting extends AbstractRoutingDataSource {

    public DataSourceRouting(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        super.setTargetDataSources(targetDataSources);
        // afterPropertiesSet()方法调用时用来将targetDataSources的属性写入resolvedDataSources中的
        super.afterPropertiesSet();
    }

    /**
     * 根据Key获取数据源的信息
     *
     * @return key
     */
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDataSourceType();
    }



    //分割线-------------------------------------------------------------------------------
    /**
     * 保存当前线程使用了事务的数据库连接(connection)
     * 当我们自己管理事务的时候即可从此处获取到当前线程使用了哪些连接从而让这些被使用的连接commit/rollback/close
     */
    private static final ThreadLocal<Copy> COPY_THREAD_LOCAL = new ThreadLocal<>();
    /**
     * mybatis在使用mapper接口执行sql的时候会从该方法获取connection执行sql
     * 如果事务是spring或者mybatis在管理，那么直接返回原生的connection
     * 如果是我们自己控制事务，则返回我们自己实现的ConnetWarp
     *
     * @return Connection
     * @throws SQLException SQLException
     */
    @Override
    public Connection getConnection() throws SQLException {
        Copy copy = COPY_THREAD_LOCAL.get();
        if (Objects.isNull(copy)){
            return determineTargetDataSource().getConnection();
        }
        Map<String, ConnectWarp> objectConnectWarpMap = copy.getConnection().get(copy.getCurrentMethod()+copy.getCount());
        copy.addCount();
        if (objectConnectWarpMap == null) {
            // 没开事物 直接返回
            return determineTargetDataSource().getConnection();
        } else {
            // 开了事物 从当前线程中拿 而且拿到的是 包装过的connect 只有手动去提交和关闭连接
            String currentName = (String) determineCurrentLookupKey();
            return objectConnectWarpMap.get(currentName);
        }
    }

    public void bindConnection(Object key) throws SQLException {
        Copy copy = COPY_THREAD_LOCAL.get();
        if (Objects.isNull(copy)){
            return;
        }
        DataSource dataSource =copy.getResolvedDataSources().get(key.toString());
        Connection connection = dataSource.getConnection();
        if (connection != null) {
            // 设置事务隔离级别
            connection.setTransactionIsolation(copy.getIsolationLevel().getValue());
            if (connection.getAutoCommit()) {
                connection.setAutoCommit(false);
            }
        }
        Map<String, ConnectWarp> connectionMap = copy.getConnection().computeIfAbsent(copy.getCurrentMethod()+copy.getCount(), k -> new HashMap<>());
        ConnectWarp connectWarp = new ConnectWarp(connection);
        connectionMap.put(key.toString(), connectWarp);
    }
    /**
     * 提交事物
     *
     * @throws SQLException SQLException
     */
    public void doCommit() throws SQLException {
        Copy copy = COPY_THREAD_LOCAL.get();
        if (Objects.isNull(copy)){
            return;
        }
        Map<String, Map<String, ConnectWarp>> copyConnection = copy.getConnection();
        if (copyConnection == null) {
            return;
        }
        for (Map.Entry<String,Map<String, ConnectWarp>> entry : copyConnection.entrySet()) {
            for (Map.Entry<String,ConnectWarp> connectWarpEntry : entry.getValue().entrySet()){
                connectWarpEntry.getValue().realCommit();
                connectWarpEntry.getValue().realClose();
            }
        }
        removeCopy();
    }
    /**
     * 回滚事物
     *
     * @throws SQLException SQLException
     */
    public void rollback() throws SQLException {
        Copy copy = COPY_THREAD_LOCAL.get();
        if (Objects.isNull(copy)){
            return;
        }
        Map<String, Map<String, ConnectWarp>> stringConnectionMap = copy.getConnection();
        if (stringConnectionMap == null) {
            return;
        }
        for (Map.Entry<String,Map<String, ConnectWarp>> entry : stringConnectionMap.entrySet()) {
            for (Map.Entry<String,ConnectWarp> connectWarpEntry : entry.getValue().entrySet()){
                connectWarpEntry.getValue().rollback();
                connectWarpEntry.getValue().realClose();
            }
        }
        removeCopy();
    }

    public void setCopy(Copy copy){
        COPY_THREAD_LOCAL.set(copy);
    }

    public void removeCopy(){
        COPY_THREAD_LOCAL.remove();
    }

    public boolean isOpen(){
        Copy copy = COPY_THREAD_LOCAL.get();
        if (Objects.isNull(copy)){
            return Boolean.FALSE;
        }
        return copy.isTranSwitch();
    }
    public Copy getCopy(){
        return COPY_THREAD_LOCAL.get();
    }


}